namespace Wigy.TimeInsight
{
    using System;
    using System.Diagnostics;
    using System.Text;

    internal class ComputerBasedTrainingHook : IDisposable
    {
        private readonly LocalWindowsHook _windowsHook = new LocalWindowsHook();

        private IntPtr _hwnd = IntPtr.Zero;
        private string _title = string.Empty;
        private string _class = string.Empty;
        private bool _isDialog;

        public event EventHandler<WindowActionEventArgs> WindowAction;
        public event EventHandler<WindowActionEventArgs> WindowCreated;
        public event EventHandler<WindowActionEventArgs> WindowDestroyed;
        public event EventHandler<WindowActionEventArgs> WindowActivated;

        public void Install()
        {
            _windowsHook.Install(NativeMethods.HookType.ComputerBasedTraining);
            _windowsHook.HookInvoked += CbtHookInvoked;
        }

        public void Dispose()
        {
            _windowsHook.Dispose();
        }

        private void CbtHookInvoked(object sender, HookEventArgs e)
        {
            var action = (ComputerBasedTrainingAction)e.HookType;
            UpdateWindowData(e.WParam);

            // Handle hook events (only a few of available actions)
            switch (action)
            {
                case ComputerBasedTrainingAction.CreateWindow:
                    FireHandler(action, WindowCreated);
                    break;
                case ComputerBasedTrainingAction.DestroyWindow:
                    FireHandler(action, WindowDestroyed);
                    break;
                case ComputerBasedTrainingAction.ActivateWindow:
                    FireHandler(action, WindowActivated);
                    break;
            }
        }

        private void FireHandler(ComputerBasedTrainingAction action, EventHandler<WindowActionEventArgs> handler)
        {
            Debug.WriteLine("WindowAction {0}: {1}({2}) {3}", action, _title, _class, _isDialog ? "Dialog" : string.Empty);

            var windowEventArgs = CreateEventArgs(action);
            handler.SafeInvoke(this, windowEventArgs);
            WindowAction.SafeInvoke(this, windowEventArgs);
        }

        private void UpdateWindowData(IntPtr wParam)
        {
            // Cache the window handle
            _hwnd = wParam;

            // Cache the window's class name
            var sb1 = new StringBuilder { Capacity = 40 };
            NativeMethods.GetClassName(_hwnd, sb1, 40);
            _class = sb1.ToString();

            // Cache the window's title bar
            var sb2 = new StringBuilder { Capacity = 256 };
            NativeMethods.GetWindowText(_hwnd, sb2, 256);
            _title = sb2.ToString();

            // Cache the dialog flag
            _isDialog = _class == "#32770";
        }

        private WindowActionEventArgs CreateEventArgs(ComputerBasedTrainingAction action)
        {
            return new WindowActionEventArgs { Action = action, Handle = _hwnd, Title = _title, ClassName = _class, IsDialogWindow = _isDialog };
        }
    }
}
